﻿namespace Blaze.Framework.Loaders
{
    using System;
    using System.Collections.Generic;
    using Logging;

    /// <summary>
    /// 资源加载器的抽象基类。
    /// </summary>
    public abstract class AssetLoaderBase : IAssetLoader
    {
        /// <summary>
        /// 当资源加载器即将释放时触发此事件。。
        /// </summary>
        public event AssetLoaderCallback Disposing;

        /// <summary>
        /// 获取错误信息。
        /// </summary>
        public virtual string Error { get; protected set; }

        /// <summary>
        /// 获取一个值，表示加载器是否已经释放。
        /// </summary>
        public bool IsDisposed { get; private set; }

        /// <summary>
        /// 获取一个值，表示加载任务是否已经结束。
        /// </summary>
        public virtual bool IsDone { get; protected set; }

        /// <summary>
        /// 获取一个值，表示加载器是否加载成功。
        /// </summary>
        public bool IsOk
        {
            get { return Error == null; }
        }

        /// <summary>
        /// 获取一个值，表示加载器是否已准备好被释放。
        /// </summary>
        public bool IsReadyForDisposal
        {
            get { return RefCount <= 0 && !mIsResurrected; }
        }

        /// <summary>
        /// 获取一个值，表示错误是否为严重错误（打印错误日志）
        /// </summary>
        public bool IsSightlyError { get; protected set; }

        /// <summary>
        /// 获取一个值，表示加载器是否已经启动。
        /// </summary>
        public bool IsStarted { get; private set; }

        /// <summary>
        /// 获取或设置加载器的模式。
        /// </summary>
        public LoadAssetMode Mode
        {
            get { return mMode; }
            set
            {
                if (mMode == value)
                    return;
#if UNITY_EDITOR
                if (IsStarted && !IsDone)
                    mLog.WarnFormat("[AssetLoaderBase]change LoadAssetMode at load time may cause potential errors.Uri={0}", Uri);
#endif
                mMode = value;
            }
        }

        /// <summary>
        /// 获取加载进度。
        /// </summary>
        public virtual float Progress { get; private set; }

        /// <summary>
        /// 获取加载器的引用计数。
        /// </summary>
        public int RefCount
        {
            get { return mRefCount; }
            set
            {
                mRefCount = value;
                if (mRefCount < 0)
                    mLog.WarnFormat("[{0}]引用计数异常 -> {1}", GetType().Name, RefCount);
            }
        }

        /// <summary>
        /// 获取加载的结果。
        /// </summary>
        public object Result { get; private set; }

        /// <summary>
        /// 获取资源定位标识符。
        /// </summary>
        public string Uri { get; private set; }

        #region IAssetLoader Members

        /// <summary>
        /// 启动加载。
        /// </summary>
        public void Start()
        {
            mIsResurrected = false;
            if (IsDone)
            {
                performCallbacks();
                return;
            }

            if (IsStarted)
                return;
            OnStart();
            IsStarted = true;
        }

        #endregion

        #region IPoolable Members

        /// <summary>
        /// 当从池中取出该对象时调用此方法。
        /// </summary>
        public virtual void OnGet()
        {
        }

        /// <summary>
        /// 当将该对象存到池中调用此方法。
        /// </summary>
        public virtual void OnPut()
        {
        }

        #endregion

        protected AssetLoaderBase()
        {
            mFinishCallbacks = new List<Delegate>();
        }

        /// <summary>
        /// 增加一个加载完成的回调。
        /// </summary>
        /// <param name="callback">回调</param>
        public void AddFinishCallback<T>(AssetLoaderCallback<T> callback) where T : AssetLoaderBase
        {
            mFinishCallbacks.Add(callback);
        }

        /// <summary>
        /// 增加一个加载完成的回调。
        /// </summary>
        /// <param name="callback">回调</param>
        public void AddFinishCallback(AssetLoaderCallback callback)
        {
            mFinishCallbacks.Add(callback);
        }

        /// <summary>
        /// 释放加载器所有占用的资源。
        /// </summary>
        public void Dispose()
        {
            if (RefCount > 0)
                mLog.WarnFormat("[{0}]正在释放一个引用计数不为0的加载器", GetType().Name);
            try
            {
                if (Disposing != null)
                    Disposing(this);
                OnDispose();
            }
            catch (Exception e)
            {
                mLog.ErrorFormat("[{0}]释放资源异常 {1} stack={2}", GetType().Name, e.Message, e.StackTrace);
            }

            IsDisposed = true;
        }

        /// <summary>
        /// 移除一个加载完成的回调。
        /// </summary>
        /// <param name="callback">回调</param>
        public void RemoveFinishCallback<T>(AssetLoaderCallback<T> callback) where T : AssetLoaderBase
        {
            mFinishCallbacks.Remove(callback);
        }

        /// <summary>
        /// 移除一个加载完成的回调。
        /// </summary>
        /// <param name="callback">回调</param>
        public void RemoveFinishCallback(AssetLoaderCallback callback)
        {
            mFinishCallbacks.Remove(callback);
        }

        /// <summary>
        /// 完成加载。
        /// </summary>
        protected void Finish(string error, object result, bool isSightlyError = false)
        {
            IsSightlyError = isSightlyError;
            if (error != null && !isSightlyError)
                mLog.Error($"[{GetType().Name}]{error}");
            Error = error;
            Result = result;
            IsDone = true;
            Progress = 1;
            performCallbacks();
        }

        /// <summary>
        /// 当加载器需要被释放时调用此方法。
        /// </summary>
        protected virtual void OnDispose()
        {
            Result = null;
            IsDone = false;
        }

        /// <summary>
        /// 当加载器启动时调用此方法。
        /// </summary>
        protected virtual void OnStart()
        {
        }

        private void performCallbacks()
        {
            foreach (var callback in mFinishCallbacks)
                callback.DynamicInvoke(this);
            mFinishCallbacks.Clear();
        }

        private static readonly Log mLog = LogManager.GetLogger(typeof(AssetLoaderBase));
        private readonly List<Delegate> mFinishCallbacks;
        private bool mIsResurrected;
        private LoadAssetMode mMode;
        private int mRefCount;

        #region Call by AssetLoaderPool

        /// <summary>
        /// 从清理队列返回可用状态。
        /// </summary>
        public void Resurrect()
        {
            mIsResurrected = true;
        }

        /// <summary>
        /// 从池中创建该加载器对象，该方法由池调用。
        /// </summary>
        public void CreateFromPool(string uri)
        {
            Uri = uri;
            RefCount++;
        }

        /// <summary>
        /// 从池中获取该加载器对象，该方法由池调用。
        /// </summary>
        public void GetFromPool()
        {
            RefCount++;
            OnGet();
        }

        /// <summary>
        /// 将该加载器回收到池中，该方法由池调用。
        /// </summary>
        public void ReturnToPool()
        {
            RefCount--;
            OnPut();
        }

        #endregion
    }
}